﻿//
// core_v2.hpp
// ~~~~~~~~~~~
//

#ifndef YUTOPP_OAUTH_CORE_V2_HPP
#define YUTOPP_OAUTH_CORE_V2_HPP

#include "../../util.hpp"
#include "../version.hpp"
#include "../../utility/translator_u8_sjis.hpp"	//

#include "key.hpp"

#include <string>
#include <map>

#include <boost/assign.hpp>
#include <boost/range/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

namespace webapp
{
	namespace oauth
	{
		namespace v2
		{
			template<class Translator, class Version = version::_2_0>
			class core
			{
			public:
				typedef Translator					translator_type;
				typedef Version						version_type;

				typedef std::string					binary_type;
				typedef
					typename converter_type::encoder_type::from
				string_type;
				typedef
					std::map<string_type, string_type>
				parameter_type;
				typedef key<string_type>			key_type;

				//
				core() {}

				core( const key_type& user_key )
					: key_( user_key )
				{}

				//バージョン
				static string_type get_version()
				{
					struct wide_switch
					{
						operator std::string() const { return version_type::name(); }
						operator std::wstring() const { return version_type::wname(); }
					};

					return wide_switch();
				}

				string_type make_authorize_token(
					const string_type& response_type,
					const string_type& redirect_uri,
					const parameter_type& extension = parameter_type() ) const
				{
					//
					const parameter_type parameter = boost::assign::map_list_of
						( "response_type",		response_type )
						( "client_id",			key_.get_client_id() )
						( "redirect_uri",		redirect_uri );

					return make_parameter( parameter, extension );
				}

				string_type make_request_for_access_token(
					const string_type& grant_type,
					const string_type& p1,
					const string_type& p2,
					const parameter_type& extension = parameter_type() ) const
				{
					parameter_type parameter = boost::assign::map_list_of
						( "grant_type",		grant_type )
						( "client_id",		key_.get_client_id() )
						( "client_secret",	key_.get_client_secret() );

					//grant_typeによって求められるパラメータが異なるので処理分け
					if ( grant_type == "authorization_code" ) {
						parameter.insert( param_type::value_type( "code",			p1 ) );
						parameter.insert( param_type::value_type( "redirect_uri",	p2 ) );

					} else if ( grant_type == "password" ) {
						parameter.insert( param_type::value_type( "username",		p1 ) );
						parameter.insert( param_type::value_type( "password",		p2 ) );

					} else if ( grant_type == "assertion" ) {
						parameter.insert( param_type::value_type( "assertion_type",	p1 ) );
						parameter.insert( param_type::value_type( "assertion",		p2 ) );

					} else if ( grant_type == "refresh_token" ) {
						parameter.insert( param_type::value_type( "refresh_token",	p1 ) );
					} else {
						//
					}

					return make_parameter( parameter, extension );
				}

				//ヘッダの生成
				string_type make_header(...) const
				{
					return "OAuth " + key_.get_access_token();
				}

				//OAuthトークンを設定
				void set_token( const string_type& access_token )
				{
					key_.set_access_token( access_token );
				}

				//
				void set_key( const key_type& k ) { key_ = k; }
				const key_type& get_key() const { return key_; }

			private:
				template<class T, class U>
				string_type make_parameter( const T& parameter, const U& extension ) const
				{
					using namespace boost::adaptors;

					return util::generate_body_helper(
								boost::join( parameter, body )
								| transformed( translator_type::encoder() )
								| transformed( url::encode_pair )
							);
				}

				key_type key_;
			};
		}

		namespace container
		{
			namespace v2
			{
				class access_return
				{
				public:
					typedef std::string							string_type;
					typedef std::map<string_type, string_type>	param_type;

					access_return( const param_type& param, const bool error_flag = false )
						: value_( param )
						, is_error_( error_flag )
					{}

					const boost::optional<string_type> get_value( const string_type& index )
					{
						const param_type::const_iterator it = value_.find( index );

						if ( it != value_.end() ) {
							return (*it).second;
						} else {
							return boost::none;
						}
					}

					operator bool() const
					{
						return !is_error_;
					}

				private:
					param_type value_;
					bool is_error_;
				};
			}
		}

	} // - oauth
} // - webapp

#endif /*YUTOPP_OAUTH_CORE_V2_HPP*/
